// ---------------------------------------------------------------------------
// FILE NAME            : LuaScript.cpp
// ---------------------------------------------------------------------------
// DESCRIPTION :
//
// Simple debugging routines
// 
// ---------------------------------------------------------------------------
// VERSION              : 1.00
// DATE                 : 1-Sep-2005
// AUTHOR               : Richard Shephard
// ---------------------------------------------------------------------------
// LIBRARY INCLUDE FILES
#include <assert.h>
#include "luainc.h"
#include "LuaScript.h"
#include "LuaRestoreStack.h"
#include "LuaThis.h"
// ---------------------------------------------------------------------------

#define BEGIN_LUA_CHECK(vm)   lua_State *state = (lua_State *) vm; \
                              if (vm.Ok ()) { 
#define END_LUA_CHECK         }


//============================================================================
// int LuaCallback
//---------------------------------------------------------------------------
// Lua C-API calling that figures out which object to hand over to
//
// Parameter   Dir      Description
// ---------   ---      -----------
// lua         IN       State variable
//
// Return
// ------
// Number of return varaibles on the stack
//
// Comments
// --------
// This is the function lua calls for each C-Function that is
// registered with lua. At the time of registering the function
// with lua, we make lua record the method "number" so we can
// know what method was actually called. The lua stack is the
// following structure:
// 0: 'this' (table)
// 1 - ...: parameters passed in
//
//============================================================================
static int LuaCallback (lua_State *lua)
{
   // Locate the psudo-index for the function number
   int iNumberIdx = lua_upvalueindex (1);
   int nRetsOnStack = 0;

   bool fSuccess = false;

   // Check for the "this" table
   if (lua_istable (lua, 1))
   {
      // Found the "this" table. The object pointer is at the index 0
      lua_rawgeti (lua, 1, 0);
      
      if (lua_islightuserdata (lua, -1))
      {
         // Found the pointer, need to cast it
         CLuaScript *pThis = (CLuaScript *) lua_touserdata (lua, -1);

         // Get the method index
         int iMethodIdx = (int) lua_tonumber (lua, iNumberIdx);

         // Check that the method is correct index
         assert (!(iMethodIdx > pThis->methods ()));

         // Reformat the stack so our parameters are correct
         // Clean up the "this" table
         lua_remove (lua, 1);
         // Clean up the pThis pointer
         lua_remove (lua, -1);

         // Call the class
         nRetsOnStack = pThis->ScriptCalling (pThis->vm (), iMethodIdx);

         fSuccess = true;
      }
   }

   if (fSuccess == false)
   {
      lua_pushstring (lua, "LuaCallback -> Failed to call the class function");
      lua_error (lua);
   }

   // Number of return variables
   return nRetsOnStack;
}


//============================================================================
// CLuaScript::CLuaScript
//---------------------------------------------------------------------------
// Constructor. Sets up the lua stack and the "this" table
//
// Parameter            Dir      Description
// ---------            ---      -----------
// CLuaVirtualMachine   IN       VM to run on
//
// Return
// ------
// None.
//
//============================================================================
CLuaScript::CLuaScript (CLuaVirtualMachine& vm)
 : m_vm (vm), m_nMethods (0), m_iThisRef (0), m_nArgs (0)
{
   BEGIN_LUA_CHECK (vm)
      // Create a reference to the "this" table. Each reference is unique
      lua_newtable (state);
      m_iThisRef = luaL_ref (state, LUA_REGISTRYINDEX);
	/*Possible way of setting GETS array, need to dynamically construct from a map probably
	  http://stackoverflow.com/questions/453769/how-do-i-create-a-lua-table-in-c-and-pass-it-to-a-lua-function 
	  lua_newtable(state);
	  int top = lua_gettop(state);
	  lua_pushstring(state,"username");
	  lua_pushstring(state,"alex");
	  lua_settable(state,top);
	  lua_pushstring(state,"password");
	  lua_pushstring(state,"default");
	  lua_settable(state,top);
	  lua_pushstring(state,"id");
	  lua_pushstring(state,"2345");
	  lua_settable(state,top);
	  int test = luaL_ref(state,LUA_REGISTRYINDEX);
	  lua_rawgeti(state, LUA_REGISTRYINDEX,test);
	  lua_setglobal(state, "GETS");*/
/*	  if(count == 0) {
		  	  lua_newtable(state);
	  lua_pushnumber(state, ++count);
	  lua_pushnumber(state,count++);
	  lua_settable(state,-3);
	  lua_setglobal(state,"GET");
	  }else {
	  lua_pushnumber(state, ++count);
	  lua_pushnumber(state,count++);
	  lua_settable(state,);
	  lua_setglobal(state,"GET");
	  }*/
      // Save the "this" table to index 0 of the "this" table
      CLuaRestoreStack rs (vm);
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);
      lua_pushlightuserdata (state, (void *) this);
      lua_rawseti (state, -2, 0);
	  
   END_LUA_CHECK
}

void CLuaScript::AddGet(map<string,string> get) {
		//for(map<string,string>::iterator i = mimemap.begin();i != mimemap.end(); i++) {
	//		cout << "ext:" << i->first << "<<>>" << i->second <<" :end" << endl;
	//	}
	lua_State *state = (lua_State* ) m_vm;
	lua_newtable(state);
	int top = lua_gettop(state);
	for(map<string,string>::iterator i = get.begin(); i != get.end(); i++) {
		 lua_pushstring(state,i->first.c_str());
		 lua_pushstring(state,i->second.c_str());
		 lua_settable(state,top);
	}
	lua_setglobal(state,"GET");
}

//============================================================================
// CLuaScript::~CLuaScript
//---------------------------------------------------------------------------
// Destructor
//
// Parameter   Dir      Description
// ---------   ---      -----------
// None.
//
// Return
// ------
// None.
//
//============================================================================
CLuaScript::~CLuaScript (void)
{
   CLuaRestoreStack rs (m_vm);

   BEGIN_LUA_CHECK (m_vm)
      // Get the reference "this" table
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);

      // Clear index 0
      lua_pushnil (state);
      lua_rawseti (state, -2, 0);
   END_LUA_CHECK
}

//============================================================================
// bool CLuaScript::CompileBuffer
//---------------------------------------------------------------------------
// Compiles a given buffer
//
// Parameter   Dir      Description
// ---------   ---      -----------
// pbBuffer    IN       Data buffer
// szLen       IN       Length of buffer
//
// Return
// ------
// Success
//
//============================================================================
bool CLuaScript::CompileBuffer (unsigned char *pbBuffer, size_t szLen)
{
   assert (pbBuffer != NULL && "CLuaScript::CompileBuffer ->  pbBuffer == NULL");
   assert (szLen != 0 && "CLuaScript::CompileBuffer -> szLen == 0");
   assert (m_vm.Ok () && "VM Not OK");

   // Make sure we have the correct "this" table
   CLuaThis luaThis (m_vm, m_iThisRef);

   return m_vm.RunBuffer (pbBuffer, szLen);
}

//============================================================================
// bool CLuaScript::CompileBuffer
//---------------------------------------------------------------------------
// Compiles a given file
//
// Parameter   Dir      Description
// ---------   ---      -----------
// strFilename IN       File name to compile
//
// Return
// ------
// Success
//
//============================================================================
bool CLuaScript::CompileFile (const char *strFilename)
{
   assert (strFilename != NULL && "CLuaScript::CompileFile -> strFilename == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   // Make sure we have the correct "this" table
   CLuaThis luaThis (m_vm, m_iThisRef);

   return m_vm.RunFile (strFilename);
}

//============================================================================
// int CLuaScript::RegisterFunction
//---------------------------------------------------------------------------
// Registers a function with Lua
//
// Parameter   Dir      Description
// ---------   ---      -----------
// strFuncName IN       Function name
//
// Return
// ------
// Success
//
//============================================================================
int CLuaScript::RegisterFunction (const char *strFuncName)
{
   assert (strFuncName != NULL && "CLuaScript::RegisterFunction -> strFuncName == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   int iMethodIdx = -1;

   CLuaRestoreStack rs (m_vm);

   BEGIN_LUA_CHECK (m_vm)
      iMethodIdx = ++m_nMethods;

      // Register a function with the lua script. Added it to the "this" table
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);

      // Push the function and parameters
      lua_pushstring (state, strFuncName);
      lua_pushnumber (state, (lua_Number) iMethodIdx);
      lua_pushcclosure (state, LuaCallback, 1);
      lua_settable (state, -3);

   END_LUA_CHECK

   return iMethodIdx;
}

//============================================================================
// bool CLuaScript::SelectScriptFunction
//---------------------------------------------------------------------------
// Selects a script function to run
//
// Parameter   Dir      Description
// ---------   ---      -----------
// strFuncName IN       Function name
//
// Return
// ------
// Success
//
//============================================================================
bool CLuaScript::SelectScriptFunction (const char *strFuncName)
{
   assert (strFuncName != NULL && "CLuaScript::SelectScriptFunction -> strFuncName == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   bool fSuccess = true;

   BEGIN_LUA_CHECK (m_vm)
      // Look up function name
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);
      lua_pushstring (state, strFuncName);
      lua_rawget (state, -2);
      lua_remove (state, -2);

      // Put the "this" table back
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);

      // Check that we have a valid function
      if (!lua_isfunction (state, -2))
      {
         fSuccess = false;
         lua_pop (state, 2);
      }
      else
      {
         m_nArgs = 0;
         m_strFunctionName = strFuncName;
      }
   END_LUA_CHECK
   
   return fSuccess;
}

//============================================================================
// bool CLuaScript::ScriptHasFunction
//---------------------------------------------------------------------------
// Checks to see if a function exists
//
// Parameter      Dir      Description
// ---------      ---      -----------
// strScriptName  IN       Function name
//
// Return
// ------
// Success
//
//============================================================================
bool CLuaScript::ScriptHasFunction (const char *strScriptName)
{
   assert (strScriptName != NULL && "CLuaScript::ScriptHasFunction -> strScriptName == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   CLuaRestoreStack rs (m_vm);

   bool fFoundFunc = false;

   BEGIN_LUA_CHECK (m_vm)
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);
      lua_pushstring (state, strScriptName);
      lua_rawget (state, -2);
      lua_remove (state, -2);

      if (lua_isfunction (state, -1))
      {
         fFoundFunc = true;
      }
   END_LUA_CHECK

   return fFoundFunc;
}

//============================================================================
// void CLuaScript::AddParam
//---------------------------------------------------------------------------
// Adds a parameter to the parameter list
//
// Parameter   Dir      Description
// ---------   ---      -----------
// string      IN       string param
//
// Return
// ------
// None.
//
//============================================================================
void CLuaScript::AddParam (char *string)
{
   assert (string != NULL && "CLuaScript::AddParam -> string == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   BEGIN_LUA_CHECK (m_vm)
      lua_pushstring (state, string);
      ++m_nArgs;
   END_LUA_CHECK
}

//============================================================================
// void CLuaScript::AddParam
//---------------------------------------------------------------------------
// Adds a parameter to the parameter list
//
// Parameter   Dir      Description
// ---------   ---      -----------
// iInt        IN       int param
//
// Return
// ------
// None.
//
//============================================================================
void CLuaScript::AddParam (int iInt)
{
   assert (m_vm.Ok () && "VM Not OK");

   BEGIN_LUA_CHECK (m_vm)
      lua_pushnumber (state, (lua_Number) iInt);
      ++m_nArgs;
   END_LUA_CHECK
}

//============================================================================
// void CLuaScript::AddParam
//---------------------------------------------------------------------------
// Adds a parameter to the parameter list
//
// Parameter   Dir      Description
// ---------   ---      -----------
// fFloat      IN       float param
//
// Return
// ------
// None.
//
//============================================================================
void CLuaScript::AddParam (float fFloat)
{
   assert (m_vm.Ok () && "VM Not OK");

   BEGIN_LUA_CHECK (m_vm)
      lua_pushnumber (state, (lua_Number) fFloat);
      ++m_nArgs;
   END_LUA_CHECK
}

//============================================================================
// bool CLuaScript::Go
//---------------------------------------------------------------------------
// Runs the selected script function
//
// Parameter   Dir      Description
// ---------   ---      -----------
// nReturns    IN       Number of expected returns
//
// Return
// ------
// None.
//
//============================================================================
void CLuaScript::main() {
	assert (m_vm.Ok () && "VM Not OK");
	lua_getglobal(m_vm,"main");
	m_vm.CallFunction(0);
	
}
bool CLuaScript::Go (int nReturns /* = 0 */)
{
   assert (m_vm.Ok () && "VM Not OK");

   // At this point there should be a parameters and a function on the
   // Lua stack. Each function get a "this" parameter as default and is
   // pushed onto the stack when the method is selected

   bool fSuccess = m_vm.CallFunction (m_nArgs + 1, nReturns); //+1 for 'this' table

   if (fSuccess == true && nReturns > 0)
   {
      // Check for returns
      HandleReturns (m_vm, m_strFunctionName);
      lua_pop ((lua_State *) m_vm, nReturns);
   }

   return fSuccess;
}